package net.carinae.dev.seduku.web.panels;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import net.carinae.dev.seduku.dao.Sudoku4Dao;
import net.carinae.dev.seduku.model.Sudoku4;
import net.carinae.dev.seduku.model.Sudoku4Changes;
import net.carinae.dev.seduku.model.SudokuHelp;

import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxSubmitLink;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.panel.Panel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.model.StringResourceModel;
import org.apache.wicket.spring.injection.annot.SpringBean;
import org.apache.wicket.version.undo.Change;


// TODO: backbutton functionality

/**
 * Frontal functionality of the Seduku game.
 * 
 * @author Carlos Vara
 * @author Patricia Grao
 */
public class Sudoku4Panel extends Panel {
	
	
	// Game status variables
	protected String score = "You haven't finished any sudoku yet.";
	protected int solvedGames = 0;
	protected Sudoku4Changes proposedChanges = new Sudoku4Changes();
	protected Sudoku4 board;
	
	
	// Panel components
	protected Sudoku4Form sudoku4Form;
	protected FeedbackPanel feedback;
	protected Label scoreText;
	protected Sudoku4HelpForm sudoku4HelpForm;
	
	
	// Spring beans
	@SpringBean
	protected Sudoku4Dao sudoku4Dao;
	
	
	public Sudoku4Panel(String id) {
		super(id);
		
		this.board = this.sudoku4Dao.loadRandom();
		
		add(this.sudoku4Form = new Sudoku4Form("sudoku4form", new PropertyModel<Sudoku4>(this, "board")));
		this.sudoku4Form.setMarkupId("sudoku4form");
		this.sudoku4Form.setOutputMarkupId(true);
		//help form
		this.sudoku4HelpForm = new Sudoku4HelpForm("sudoku4HelpForm");
		this.sudoku4HelpForm.setOutputMarkupId(true);
		add(this.sudoku4HelpForm);
		add(this.feedback = new FeedbackPanel("feedback"));
		this.feedback.setMarkupId("feedback");
		this.feedback.setOutputMarkupId(true);
		add(this.scoreText = new Label("score", new PropertyModel<String>(this, "score")));
		this.scoreText.setOutputMarkupId(true);
	}
	
	
	protected boolean applyChanges() {
		
		// Apply the proposed changes
		HashMap<String, ArrayList<Object[]>> errors = this.board.applyChanges(this.proposedChanges);
		
		// Reset the changes object
		this.proposedChanges.reset();
		
		// Inform the user of the encountered problems, if there were any
		for ( String index : errors.keySet() ) {
			for ( Object[] errorObjects : errors.get(index) ) {
				if ( errorObjects.length > 1 ) {
					Object[] params = Arrays.copyOfRange(errorObjects, 1, errorObjects.length);
					error("Square " + index + ": " + new StringResourceModel(errorObjects[0].toString(), this, null, params).getString());
				}
				else {
					error("Square " + index + ": " + new StringResourceModel(errorObjects[0].toString(), this, null).getString());
				}
			}
		}
		
		// If it's solved, create a new one
		boolean finished = false;
		if ( this.board.isSolved() ) {
			//this.board = this.sudoku4Dao.loadRandom();
			this.score = "Great! You have solved " + ++this.solvedGames + " sudokus.";
			finished = true;
		}
		
		return finished;
	}
	
	/**
	 * The form implements a help panel which guides to the user
	 * to complete the sudoku
	 * @author patricia
	 *
	 */
	public class Sudoku4HelpForm extends Form {
		protected Sudoku4HelpModel listmessagehelp = new Sudoku4HelpModel();
		protected ListView<SudokuHelp> listhelp;
				
		public Sudoku4HelpForm(String id){
			super(id);
			this.listhelp = new ListView<SudokuHelp>("listhelp", listmessagehelp){
				
				@Override
				protected void populateItem(ListItem<SudokuHelp> item) {
					
						item.add(new Label("messagehelp", ((SudokuHelp) item.getModelObject()).getMessage()));
			    }
			};
			this.listhelp.setOutputMarkupId(true);
			add(this.listhelp);
			
			add(new AjaxSubmitLink("advice") {
		        @Override
		        protected void onSubmit(AjaxRequestTarget target, Form form) {
		        	String squareMark = lookforMessageHelp();
		            target.addComponent(Sudoku4Panel.this.sudoku4HelpForm);
		            target.appendJavascript("processServerResponseHelpColor('" + squareMark + "');");
		        }
		    });
			
//			add(new Button("") {
//				
//				@Override
//				public void onSubmit() {
//					lookforMessageHelp();			
//				}
//			});
			
		}
		
		//Consulting the model to retrieve the help message, depending on 
		//listHelp's state, the message to the user is adapted 
		private String lookforMessageHelp (){
			// Apply the proposed changes
			HashMap<String, ArrayList<Object[]>> advice = Sudoku4Panel.this.board.nextSquareHelp();
			String squareMark = "";
			
			if (!advice.isEmpty()){
				squareMark = advice.keySet().toArray()[0].toString();
				
				//decide the message help type, at the moment only can be two: 
				//Firstly: report the user the square in which pays attention
				//Secondly: report the user the number to put into the square
				String messsageshow;
				if (this.listmessagehelp.getObject().isEmpty()){
					messsageshow = "help.mess.square";
				}else if (this.listmessagehelp.getObject().size() == 1 && 
						this.listmessagehelp.getObject(0).getSquare().equals( squareMark ) ){
					messsageshow = "help.mess.number";
				}else{
					//come at the first step, but clear the listmessagehelp - size == 2
					listmessagehelp.setObject();
					messsageshow = "help.mess.square";
				}
				
				// Inform the user help message
				for ( String index : advice.keySet() ) {
					for ( Object[] errorObjects : advice.get(index) ) {
						if ( errorObjects.length > 1 ) {
							Object[] params = Arrays.copyOfRange(errorObjects, 1, errorObjects.length);
							//set the next message in the help list		
							if (messsageshow.equals("help.mess.square")){
								this.listmessagehelp.setObject(new SudokuHelp(index, new StringResourceModel(messsageshow, this, null, params).getString() + index));
							}else{
								this.listmessagehelp.setObject(new SudokuHelp(index, new StringResourceModel(messsageshow, this, null, params).getString() + errorObjects[0].toString()));
							}
						}
						else {
							if (messsageshow.equals("help.mess.square")){
								this.listmessagehelp.setObject(new SudokuHelp(index, new StringResourceModel(messsageshow, this, null).getString() + index));
							}else{
								this.listmessagehelp.setObject(new SudokuHelp(index, index + new StringResourceModel(messsageshow, this, null).getString() + errorObjects[0].toString()));
							}
						}
					}
				}
			}else{
				listmessagehelp.setObject();
			}
			
			return squareMark;
		}
		
		// Model of a message help
		public class Sudoku4HelpModel extends Model<ArrayList<SudokuHelp>> {
			
			protected final ArrayList<SudokuHelp> messageHelp = new ArrayList<SudokuHelp>();
						
			public Sudoku4HelpModel() {	}
						
			@Override
			public ArrayList<SudokuHelp> getObject() {
				return this.messageHelp;
			}
			
			public SudokuHelp getObject(int index) {
				return (index >= 0 && index < this.messageHelp.size()) ? this.messageHelp.get(index) : new SudokuHelp("", "");
			}

			@Override
			public void setObject(ArrayList<SudokuHelp> object) {
				this.messageHelp.clear();
				this.messageHelp.addAll(object);
			}
			
			public void setObject(SudokuHelp object) {
				this.messageHelp.add(object);
			}
			
			public void setObject() {
				this.messageHelp.clear();
			}
			
		}
	}
	
	
	// The form
	// Maybe its model should be the changes instead of the board??
	// And adapt the squares model to be the board??
	public class Sudoku4Form extends Form<Sudoku4> {

		public Sudoku4Form(String id, IModel<Sudoku4> model) {
			super(id,model);
			
			// Add inputs for all the squares
			for ( String inputId : Sudoku4.SQUARES ) {
				final TextField<String> temp;
				add(temp = new TextField<String>(inputId, new Sudoku4SquareModel(inputId)) {

					@Override
					public boolean isEnabled() {
						return Sudoku4Panel.this.board.isEnabled(this.getId());
					}
					
				});

				temp.setMarkupId(inputId);
				temp.setOutputMarkupId(true);
				temp.add(new AjaxFormComponentUpdatingBehavior("onChange") {

					@Override
					protected void onUpdate(AjaxRequestTarget target) {
						
						Sudoku4Panel.this.addStateChange(new Sudoku4Change(Sudoku4Panel.this.board));
						
						boolean finished = applyChanges();
				
						// Always refresh feedback messages
						target.addComponent(Sudoku4Panel.this.feedback);
						
						if ( finished ) {
							target.addComponent(Sudoku4Panel.this.scoreText);
							target.addComponent(Sudoku4Panel.this.sudoku4Form);
							target.appendJavascript("showWin()");
						}
						else {
							target.addComponent(temp);
							target.appendJavascript("processServerResponse('" + target.getLastFocusedElementId() + "');");
						}
						
					}
					
				});
			}
			
			// Submit button
			add(new Button("check"));
			add(new Button("newSudoku") {

				@Override
				public void onSubmit() {
					Sudoku4Panel.this.board = Sudoku4Panel.this.sudoku4Dao.loadRandom();
				}
			});
			
		}
		
		
		@Override
		protected void onSubmit() {
			addStateChange(new Sudoku4Change(Sudoku4Panel.this.board));
			applyChanges();
		}


		// Model of a square
		public class Sudoku4SquareModel extends Model<String> {
			
			protected final String myId;
			
			public Sudoku4SquareModel(String id) {
				this.myId = id;
			}
						
			@Override
			public String getObject() {
				return Sudoku4Panel.this.board.getCurrentSquareRep(this.myId);
			}

			@Override
			public void setObject(String object) {
				Sudoku4Panel.this.proposedChanges.setSquare(this.myId, object);
			}
			
		}
		
	}
	
	private class Sudoku4Change extends Change {
		
		final Sudoku4 sudoku4;

		public Sudoku4Change(Sudoku4 sudoku4) {
			this.sudoku4 = new Sudoku4(sudoku4);
		}
		
		@Override
		public void undo() {
			Sudoku4Panel.this.board = this.sudoku4;
		}
		
	}

}
